#include "Terrain.h"

Terrain::Terrain(void)
{
}

Terrain::~Terrain(void)
{
}
void Terrain::drawTerrain()
{
	glBindTexture(GL_TEXTURE_2D, mTextureId);

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	
	glTexImage2D(GL_TEXTURE_2D, 0, 3, mSurface->w, mSurface->h, 0, 0x80E0/*GL_BGR*/, GL_UNSIGNED_BYTE, mSurface->pixels);
	glCallList(mId);
}
bool Terrain::loadTerrainFromTGAImage(string textureImageFilePath, string imageFilePath, int terrainWidth, int terrainHeight, float vertexHeightFactor)
{
	setTextureParameters(textureImageFilePath);

	mVertexHeightFactor = vertexHeightFactor;

	ImageIO imageIO;
	Image image;

	bool load = imageIO.load(imageFilePath.c_str(), image);

	if(load)
	{
		fillDisplayLists(image,  terrainWidth,  terrainHeight);
	}
	else
	{
		cout<<"Could not load image file "<<imageFilePath<<endl;
	}
	
	return load;
}
void Terrain::fillDisplayLists(Image& image, int terrainWidth, int terrainHeight)
{
	float w = image.getWidth();
	float h = image.getHeight();

	// Initialice local variables
	// Distance between vertices
	float xDelta = terrainWidth	 / ( w - 1) ;
	float yDelta = terrainHeight / (h - 1);

	// First vertex position (top left vertex if seen from above)
	vec2 initPos(	/*X*/	-terrainWidth/2,
					/*Y*/	-terrainHeight/2 );

	// Total number of vertices
	int nmbVertices = w * h * 3;

	// Vertices coordinates arrays
	// Vertices color coordinates arrays
	// Vertices normals coordinates arrays
	float* vertices = new float[	nmbVertices	];
	float* uv		= new float[	w * h * 2	];
	float* normals	= new float[	nmbVertices	];

	// Vertices index array. each row of vertices will form one triangle strip
	int ih = h - 1;
	int iw = w * 2;
	vector<unsigned int*> indexes;

	// Index for the vertice arrays
	int arrayIndex = 0;
	int uvArrayIndex = 0;
	int normalIndex = 0;





	for(int i =  0 ; i< h ; i++)
	{
		unsigned int* rowIndex = new unsigned int[iw];
		int dispIndex = 0;

		for(int j =  0 ; j< w ; j++)
		{
			Image::Color pixelColor = image.getValue(j,i);
			vec3 coord;
			vec3 normal;
			calculateNormalAndCoord(&coord, &normal, initPos, i,j, xDelta, yDelta, image);

			vertices[	arrayIndex 		] = coord.x;
			vertices[	arrayIndex + 1	] = coord.y;
			vertices[	arrayIndex + 2	] = coord.z;
			
			uv[		uvArrayIndex 		] =j*0.01/*(float)j/(float)(w-1)*/;
			uv[		uvArrayIndex + 1	] =i*0.01/*(float)i/(float)(h-1)*/;

			normals[	arrayIndex 		] = normal.x;
			normals[	arrayIndex + 1	] = normal.y;
			normals[	arrayIndex + 2	] = normal.z;

			arrayIndex+=3;
			uvArrayIndex+=2;

			if( i != h - 1 )
			{
				rowIndex[	dispIndex		] = j + i*w;
				rowIndex[	dispIndex+1		] = j + (i+1)*w;

				dispIndex+=2;
			}
		}

		if( i != h - 1 )
		{
			indexes.push_back(rowIndex);
		}
	}


	// Actual building the display lists
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, uv);
	glNormalPointer( GL_FLOAT, 0, normals);

	

	mId = glGenLists(1);

	glNewList(mId, GL_COMPILE);
	
	
	for(int k =0; k < indexes.size() ; k++)
	{
		glDrawElements(GL_TRIANGLE_STRIP, iw, GL_UNSIGNED_INT, indexes.at(k) );	
	}
	
	
	glEndList();

	//delete [] vertices;
	//delete [] uv;
	//delete [] normals;
	//for(k =0; k < indexes.size() ; k++)
	//{
	//	delete [] indexes.at(k);
	//}
}
bool Terrain::calculateVertexCoordinates(vec3* coord, vec2 initPos, int i, int j, float xDelta, float yDelta, Image& image)
{
	if(i >=0 && j >=0 && j < image.getWidth() && i < image.getHeight())
	{
		Image::Color pixelColor = image.getValue(j,i);

		coord->x =	initPos[0] + j * xDelta;
		coord->y =	initPos[1] + i * yDelta;
		coord->z =	( pixelColor.r + pixelColor.g + pixelColor.b ) * mVertexHeightFactor;

		return true;
	}
	else 
	{
		return false;
	}
}
void Terrain::calculateNormalAndCoord(vec3* vertexCoordinates,vec3* normalCoordinates, vec2 initPos, int i, int j, float xDelta, float yDelta, Image& image )
{
	calculateVertexCoordinates(vertexCoordinates, initPos,i,	j,	xDelta, yDelta, image);

	vec3 up;
	vec3 left;
	vec3 down;
	vec3 right;
	vec3 downLeft;
	vec3 upRight;
	bool upCoord = calculateVertexCoordinates(		&up,		initPos,	i-1	,j	,xDelta, yDelta, image);
	bool leftCoord = calculateVertexCoordinates(	&left,		initPos,	i	,j-1,xDelta, yDelta, image);
	bool downCoord = calculateVertexCoordinates(	&down,		initPos,	i+1	,j	,xDelta, yDelta, image);
	bool rightCoord = calculateVertexCoordinates(	&right,		initPos,	i	,j+1,xDelta, yDelta, image);
	bool downLeftCoord = calculateVertexCoordinates(&downLeft,	initPos,	i+1	,j-1,xDelta, yDelta, image);
	bool upRightCoord = calculateVertexCoordinates(	&upRight,	initPos,	i-1	,j+1,xDelta, yDelta, image);

	int nmbTriangles = 0;

	vec3 v1;
	vec3 v2;
	vec3 v3;
	vec3 cross;

	float average[3] = {0.0, 0.0, 0.0 };

	if(leftCoord != NULL)
	{
		v2 = left - *vertexCoordinates;
		if(upCoord != NULL)
		{
			v1 = up - *vertexCoordinates;			

			cross = v2^v1;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;

			nmbTriangles++;

		}
		if(downCoord != NULL)
		{
			v1 = down - *vertexCoordinates;
			v3 = downLeft - *vertexCoordinates;

			cross = v3^v2;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;
			
			cross = v1^v3;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;

			nmbTriangles+=2;

		}
	}

	if(rightCoord != NULL)
	{
		v2 = right - *vertexCoordinates;
		
		if(upCoord != NULL)
		{
			v1 = up - *vertexCoordinates;
			v3 = upRight - *vertexCoordinates;
			
			cross = v3^v2;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;

			cross = v1^v3;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;

			nmbTriangles+=2;

		}
		if(downCoord != NULL)
		{
			v1 = down - *vertexCoordinates;			

			cross = v2^v1;

			average[0]+=cross.x;
			average[1]+=cross.y;
			average[2]+=cross.z;

			nmbTriangles++;

		}
	}

	normalCoordinates->x = average[0]/nmbTriangles;
	normalCoordinates->y = average[1]/nmbTriangles;
	normalCoordinates->z = average[2]/nmbTriangles;
	normalCoordinates->normalize();

}
void Terrain::setTextureParameters(string pTextImagePath)
{
	mSurface = SDL_LoadBMP(pTextImagePath.c_str());
	if(!mSurface)
	{
		cerr << "Could not load texture : " << pTextImagePath << endl;
	}
}

void Terrain::setTextureId(GLuint pTextureId)
{
	mTextureId = pTextureId;
}
GLuint Terrain::getTextureId()
{
	return mTextureId;
}